// 导入所需的Java类库，这些库提供了图形界面、脚本计算、音频处理等功能
import java.awt.Color;  // 导入颜色类，用于设置界面元素颜色
import javax.script.ScriptEngine;  // 导入脚本引擎接口，用于计算数学表达式
import javax.script.ScriptEngineManager;  // 导入脚本引擎管理器，用于获取脚本引擎
import javax.script.ScriptException;  // 导入脚本异常类，用于处理计算异常
import javax.sound.sampled.AudioFormat;  // 导入音频格式类，用于定义音频参数
import javax.sound.sampled.AudioInputStream;  // 导入音频输入流类，用于读取音频数据
import javax.sound.sampled.AudioSystem;  // 导入音频系统类，用于音频处理
import javax.sound.sampled.Clip;  // 导入音频剪辑类，用于播放音频
import javax.swing.BorderFactory;  // 导入边框工厂类，用于创建界面边框
import javax.swing.ButtonModel;  // 导入按钮模型类，用于处理按钮状态
import javax.swing.JButton;  // 导入按钮类，用于创建界面按钮
import javax.swing.JFrame;  // 导入框架类，用于创建主窗口
import javax.swing.JPanel;  // 导入面板类，用于组织界面组件
import javax.swing.JScrollPane;  // 导入滚动面板类，用于为文本区域添加滚动条
import javax.swing.JTextArea;  // 导入文本区域类，用于显示多行文本
import javax.swing.JTextField;  // 导入文本字段类，用于显示和输入单行文本
import javax.swing.SwingUtilities;  // 导入Swing工具类，提供Swing组件的实用方法

// 在原有import基础上添加这两个类，用于图形绘制和图标显示
import java.awt.Graphics;  // 导入图形类，用于自定义绘制
import javax.swing.ImageIcon;  // 导入图像图标类，用于显示图像

import java.awt.BorderLayout;  // 导入边界布局类，用于面板布局
import java.awt.Dimension;  // 导入尺寸类，用于设置组件尺寸
import java.awt.Font;  // 导入字体类，用于设置文本字体
import java.awt.GridLayout;  // 导入网格布局类，用于面板布局
import java.awt.Toolkit;  // 导入工具包类，用于获取系统工具
import java.awt.event.ActionEvent;  // 导入动作事件类，用于处理按钮点击事件
import java.awt.event.ActionListener;  // 导入动作监听器接口，用于监听按钮点击
import java.awt.event.MouseAdapter;  // 导入鼠标适配器类，用于简化鼠标事件处理
import java.awt.event.MouseEvent;  // 导入鼠标事件类，用于处理鼠标操作
import java.io.ByteArrayInputStream;  // 导入字节数组输入流类，用于从字节数组读取数据
import java.io.File;  // 导入文件类，用于操作文件
import java.util.ArrayList;  // 导入ArrayList类，用于创建动态数组
import java.util.List;  // 导入List接口，用于定义列表
import javax.swing.Timer;  // 添加这行
// ApplicationListener是Java中事件处理机制的核心部分
// 为了建立计算器组件和用户操作之间的响应机制
// Calculator类继承JFrame类，成为一个窗口应用程序，并实现ActionListener接口处理按钮点击事件
public class Calculator extends JFrame implements ActionListener {
    // 新增的颜色常量定义（放在类开头），使用final修饰表示常量，值不可改变
    private final Color DISPLAY_BG = new Color(240, 240, 240);  // 显示区背景色（浅灰）
    private final Color NUMBER_BG = new Color(245, 245, 245);   // 数字按钮背景
    private final Color OPERATOR_BG = new Color(220, 220, 220); // 运算符背景
    private final Color SPECIAL_BG = new Color(200, 200, 200);  // 特殊功能按钮背景
    private final Color EQUAL_BG = new Color(65, 105, 225);     // 等号按钮背景（蓝色）
    private final Color EQUAL_FG = Color.WHITE;                 // 等号文字颜色（白）
    
    // 定义历史记录相关组件和数据结构
    private JPanel historyPanel;      // 历史记录面板，用于显示计算历史
    private JTextArea historyArea;    // 显示历史记录的文本区域
    private List<String> historyList = new ArrayList<>(); // 存储历史记录的列表，使用泛型指定存储String类型
    
    // 定义音频相关变量，用于按钮音效和音乐播放
    private Clip buttonSound;  // 按钮点击音效对象
    private Clip music;       // 音乐播放对象  <-- 添加这行
    private Timer timer;  // 添加在类的成员变量声明处
    // 声明页面组件面板，用于组织界面元素
    private JPanel north, center;
    // 文本框，用于展示计算过程、结果或者日期时间
    private JTextField field;
    // 按钮数组，存储计算器上的所有按钮
    private JButton[] btns;
    
    // 构造函数，用于初始化计算器窗口和组件
    public Calculator() {
        // 设置窗口标题
        setTitle("计算器");
        // 设置窗口大小（宽度500，高度600）
        setSize(500, 600);
        // 设置窗口关闭时退出程序
        //setDefaultCloseOperation(EXIT_ON_CLOSE) 
        //是 Java Swing 中用于设置窗口关闭行为的方法。
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // 设置窗口居中显示
        setLocationRelativeTo(null);
        // 初始化音效
        loadButtonSound();  
        // 初始化界面组件
        initComponent();
        // 设置窗口可见
        setVisible(true);
    }
    
    // 初始化界面组件的方法
    private void initComponent() {
        // 创建顶部面板和中间面板
        north = new JPanel();
        center = new JPanel();
        // 创建文本字段，用于显示计算过程和结果
        //TextField 是 Swing 组件库中的一个类，用于创建单行文本输入框。
        field = new JTextField();
        // 设置输入框大小为宽400，高100
        field.setPreferredSize(new Dimension(400, 100)); // 更合理的大小
        // 设置文本右对齐
        field.setHorizontalAlignment(JTextField.RIGHT);  // 文字右对齐
        // 设置输入框内边距（上、左、下、右各10像素）
        field.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 
        // 禁用文字输入，禁止通过电脑键盘编辑
        field.setEditable(false);
        // 设置字体为SansSerif，粗体，大小36
        field.setFont(new Font("SansSerif", Font.BOLD, 36));
        
        // 定义按钮上的文本内容，按行排列
        String[] str = new String[]{
            "7", "8", "9", "+", "x²", "sin",  // 第1行：数字7、8、9，加号，平方，正弦
            "4", "5", "6", "-", "√",   "cos", // 第2行：数字4、5、6，减号，平方根，余弦
            "1", "2", "3", "×", "1/x", "tan", // 第3行：数字1、2、3，乘号，倒数，正切
            "0", ".", "=", "÷", "π",   "log", // 第4行：数字0，小数点，等号，除号，圆周率，对数
            "C", "B", "T", "S", "%",   "ln"   // 第5行：清除，退格，时间，音乐，百分比，自然对数
        };
        // 创建按钮数组，长度与str数组相同
        //这句话的作用是创建一个长度为 str.length 的 JButton 数组，用于存储多个按钮对象。
        btns = new JButton[str.length];
        // 循环创建每个按钮并设置属性
        for (int i = 0; i < btns.length; i++) {
            // 1. btnType 是一个临时变量，用于存储当前按钮的文本内容
            final String btnType = str[i];
            //创建一个新的按钮对象，按钮上显示的文本为 btnType
            //new JButton(btnType)：调用 JButton 的构造函数，创建一个带文本的按钮
            final JButton currentBtn = new JButton(btnType);
            //将新创建的按钮对象 currentBtn 存入按钮数组 btns 的第 i 个位置
            btns[i] = currentBtn;

            // 2. 设置按钮字体为SansSerif，粗体，大小20
            currentBtn.setFont(new Font("SansSerif", Font.BOLD, 20));

            // 3. 根据按钮类型设置不同的背景颜色
            //btnType.matches("[0-9.]") 是 Java 中的正则表达式匹配方法，
            //用于判断字符串 btnType 是否为 单个数字字符（0-9）或小数点（.）
            if (btnType.matches("[0-9.]")) {
                // 数字和小数点按钮使用NUMBER_BG颜色
                currentBtn.setBackground(NUMBER_BG);
            } else if (btnType.equals("=")) {
                // 等号按钮使用EQUAL_BG颜色和EQUAL_FG文字颜色
                currentBtn.setBackground(EQUAL_BG);
                currentBtn.setForeground(EQUAL_FG);
            } else if (btnType.matches("[+\\-×÷%]")) {
                // 运算符按钮使用OPERATOR_BG颜色
                currentBtn.setBackground(OPERATOR_BG);
            // 检查当前按钮是否是 "C"（清除按钮）
            } else if (btnType.equals("C")) {
            // 为 "C" 按钮添加鼠标监听器（MouseListener），用于检测长按操作
            currentBtn.addMouseListener(new MouseAdapter() {
             // 定义一个变量，用于记录鼠标按下的时间（单位：毫秒）
                private long pressTime;

             // 当鼠标按下时触发的方法
             @Override
             public void mousePressed(MouseEvent e) {
            // System.currentTimeMillis() 获取当前系统时间（毫秒级时间戳）
            // 记录按下按钮的时刻，存入 pressTime 变量
                 pressTime = System.currentTimeMillis();
             }

        // 当鼠标释放时触发的方法
             @Override
             public void mouseReleased(MouseEvent e) {
            // 获取鼠标释放时的当前时间
            long releaseTime = System.currentTimeMillis();
            
            // 计算从按下到释放的时间差（即长按的持续时间）
            long pressDuration = releaseTime - pressTime;

            // 判断是否长按超过 1 秒（1000 毫秒）
            if (pressDuration >= 1000) {
                // 调用 clearHistory() 方法清空历史记录
                clearHistory();
                
                // 在计算器主显示框（field）显示提示信息（可选）
                field.setText("历史已清空");
                
                // 注：这里可以添加其他反馈，比如播放音效：
                // playButtonSound();
            }
        }
    });

            }else{
                // 特殊功能按钮使用SPECIAL_BG颜色
                currentBtn.setBackground(SPECIAL_BG);
            }

            // 4. 设置按钮边框为凸起斜面边框，增强立体感
            //createRaisedBevelBorder()
            //createRaisedBevelBorder创建一个凸起的斜面边框
            //BorderFactory是 Swing 提供的一个工厂类，用于创建各种预定义的边框样式
            //无需实例化，直接通过静态方法调用（如 BorderFactory.createRaisedBevelBorder()）
            //currentBtn.setBorder(...)：将创建的边框应用到当前按钮
            currentBtn.setBorder(BorderFactory.createRaisedBevelBorder());
            // 取消按钮获得焦点时的绘制效果
            currentBtn.setFocusPainted(false);
            
            // 5. 添加按压动画，监听按钮模型的变化
            //e.getSource() 返回触发事件的对象（这里是按钮）
            //ButtonModel 是按钮的状态模型，包含按钮的各种状态（按下、释放、选中、禁用等）
            
            //ButtonModel 是按钮的状态模型，存储按钮的各种状态信息（如是否被按下、选中、启用等）
            //所有 Swing 按钮（如 JButton、JCheckBox）都有一个关联的 ButtonModel
            currentBtn.getModel().addChangeListener(e -> {
                // 如果是 C 按钮，直接返回，不处理颜色变化
                  if (btnType.equals("C")) return;
                ButtonModel model = (ButtonModel)e.getSource();
                
                
                if (model.isPressed()) {
                    //true：按钮当前正在被按下（鼠标或键盘按下未释放）
                    //false：按钮未被按下（正常状态或已释放）
                    // 按钮被按下时，设置凹陷边框并使背景色变暗
                    currentBtn.setBorder(BorderFactory.createLoweredBevelBorder());
                    currentBtn.setBackground(currentBtn.getBackground().darker());
                } else {
                    // 按钮释放时，恢复凸起边框和原来的背景色
                    currentBtn.setBorder(BorderFactory.createRaisedBevelBorder());
                    // 根据按钮类型设置不同的背景色，增强界面视觉区分度
                if (btnType.matches("[0-9.]")) {  
                    // 使用正则表达式判断是否为数字或小数点
                    // 数字按钮（0-9）和小数点按钮使用数字背景色
                    // NUMBER_BG通常是浅色（如白色或浅灰色），方便用户快速识别数字区域
                    currentBtn.setBackground(NUMBER_BG);
                } else if (btnType.equals("=")) {  
                    // 判断是否为等号按钮
                    // 等号按钮使用特殊背景色（如蓝色），突出其作为计算结果触发按钮的重要性
                    // EQUAL_BG通常是对比鲜明的颜色，吸引用户注意
                    currentBtn.setBackground(EQUAL_BG);
                } else if (btnType.matches("[+\\-×÷%]")) { 
                     // 判断是否为运算符按钮
                    // 运算符按钮（加、减、乘、除、百分比）使用运算符背景色
                    // OPERATOR_BG通常是中等深度的颜色（如灰色或橙色），与数字按钮区分
                    currentBtn.setBackground(OPERATOR_BG);
                } else {  
                    // 其他特殊功能按钮（如清除、平方、开方等）
                    // 特殊按钮使用特殊背景色
                    // SPECIAL_BG通常是区别于数字和运算符的颜色（如红色或紫色）
                    currentBtn.setBackground(SPECIAL_BG);
                }

                }
            });

            // 6. 添加按钮到中间面板，并为按钮添加动作监听器
            center.add(currentBtn);
            currentBtn.addActionListener(this);
        }
        // 设置顶部面板使用边界布局
        //north.setLayout(new BorderLayout()); 是 Java Swing 中设置容器布局管理器的代码，
        //用于指定名为 north 的容器使用 边界布局（BorderLayout）
        north.setLayout(new BorderLayout());
        // 为中间面板设置四周留白（各10像素）
        center.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 
        // 设置中间面板使用5行6列的网格布局，组件之间水平和垂直间距各5像素
        center.setLayout(new GridLayout(5, 6, 5, 5));
        // 将文本字段添加到顶部面板的中心位置
        north.add(field, BorderLayout.CENTER);
        
        // 初始化历史记录面板
        //在 Java Swing 中，historyPanel = new JPanel(); 
        //这行代码用于创建一个新的面板组件
        historyPanel = new JPanel();
        // 设置历史记录面板使用边界布局
        historyPanel.setLayout(new BorderLayout());
        // 设置历史记录面板的边框为带标题的边框，标题为"历史记录"
        historyPanel.setBorder(BorderFactory.createTitledBorder("历史记录"));
        // 设置历史记录面板的首选尺寸为宽130，高600
        historyPanel.setPreferredSize(new Dimension(130, 600)); 
        
        // 初始化历史记录文本区域
        historyArea = new JTextArea();
        // 禁止编辑历史记录
        historyArea.setEditable(false); 
        // 设置历史记录文本区域的字体为SansSerif，常规样式，大小14
        historyArea.setFont(new Font("SansSerif", Font.PLAIN, 14));
        // 添加鼠标监听器，处理鼠标点击事件
        historyArea.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 1) { // 单击选中
                    try {
                        // 获取点击位置的模型坐标
                        @SuppressWarnings("deprecation")  // 忽略过时警告
                       int caretPos = historyArea.getUI().viewToModel(historyArea, e.getPoint());
                        //获取historyArea中文本光标（caret）所在行的起始字符位置（偏移量）
                        int start = historyArea.getLineStartOffset(historyArea.getLineOfOffset(caretPos));
                        // 获取点击位置所在行的结束偏移量
                        int end = historyArea.getLineEndOffset(historyArea.getLineOfOffset(caretPos));
                        // 获取选中的行文本并去除首尾空格
                        String selectedLine = historyArea.getText(start, end - start).trim();
                        
                        // 提取表达式部分（去掉 "= xxx"）
                        if (selectedLine.contains("=")) {
                            // 按等号分割，取第一部分作为表达式
                            //trim() 移除表达式前后可能存在的空格
                            //用户点击历史记录中的 "3 + 5 = 8
                            //程序提取 "3 + 5" 并填入输入框
                            //用户可以直接修改表达式（如改为 "3 + 6"）重新计算
                            String expression = selectedLine.split("=")[0].trim();
                            // 将表达式设置到输入框
                            field.setText(expression);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });
        // 为历史记录文本区域添加滚动条
        JScrollPane scrollPane = new JScrollPane(historyArea); 
        // 将滚动面板添加到历史记录面板的中心位置
        historyPanel.add(scrollPane, BorderLayout.CENTER);
        
        // 将历史记录面板添加到主窗口的右侧
        add(historyPanel, BorderLayout.EAST);
        
        // 将顶部面板添加到主窗口的北部
        add(north, BorderLayout.NORTH);
        // 将中间面板添加到主窗口的中心
        add(center);
    }
    
    // 加载按钮音效的方法
    private void loadButtonSound() {
        try {
            // 获取音频剪辑对象
            buttonSound = AudioSystem.getClip();
            // 定义音频格式：采样率44100Hz，16位深度，单声道，大端字节序，非立体声
            AudioFormat format = new AudioFormat(44100, 16, 1, true, false);
            // 创建音频数据字节数组，长度3000字节，约70ms时长
            byte[] data = new byte[3000]; 
            // 生成音频数据（正弦波）
            for (int i = 0; i < data.length; i++) {
                double progress = i / (double) data.length;
                // 混合基础频率（1600Hz）和泛音（2400Hz）
                double wave = 0.7 * Math.sin(2 * Math.PI * 1600 * progress) 
                            + 0.3 * Math.sin(2 * Math.PI * 2400 * progress);
                // 指数衰减包络，使声音逐渐减弱
                double envelope = Math.exp(-progress * 8); 
                // 将计算得到的波形值转换为字节（音量降低）
                data[i] = (byte) (wave * envelope * 60); 
            }
            // 从字节数组创建音频输入流
            AudioInputStream ais = new AudioInputStream(
                new ByteArrayInputStream(data), format, data.length);
            // 打开音频流并加载到音频剪辑
            buttonSound.open(ais);
        } catch (Exception e) {
            // 异常处理：如果加载自定义音效失败，使用默认蜂鸣声
            System.out.println("使用默认蜂鸣声替代");
            Toolkit.getDefaultToolkit().beep(); 
        }
    }
    
    // 播放按钮音效的方法
    private void playButtonSound() {
        if (buttonSound != null) {
            // 停止当前播放
            buttonSound.stop();          
            // 重置到音效开头
            buttonSound.setFramePosition(0); 
            // 开始播放音效
            buttonSound.start();          
        }
    }
    // 新增：清空历史记录方法
    private void clearHistory() {
        //historyList.clear();
        historyArea.setText("");
    }

    
    // 设置按钮文本的方法，根据不同按钮执行不同操作
    private void setBtnText(String text) {
        // 获取当前文本框中的内容
        String currentText = field.getText();  
        // 根据按钮文本执行不同操作
        switch (text) {
           case "x²":
    if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            double result = num * num;
            field.setText(String.valueOf(result));
            historyArea.append("(" + currentText + ")² = " + result + "\n");
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
            case "√":
    if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            if (num < 0) {
                field.setText("无效的输入");
            } else {
                double result = Math.sqrt(num);
                field.setText(String.valueOf(result));
                historyArea.append("√" + currentText + " = " + result + "\n");
            }
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
            case "1/x":
    if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            if (num == 0) {
                field.setText("Infinity");
            } else {
                double result = 1 / num;
                field.setText(String.valueOf(result));
                historyArea.append("1/(" + currentText + ") = " + result + "\n");
            }
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
            case "π":  // 圆周率Pi功能
                // 直接设置Math.PI的值到文本框
                //Math.PI 是 Java 内置的数学常量，属于 java.lang.Math 类
                field.setText(String.valueOf(Math.PI)); 
                break;
            case "C":  // 清除功能
                // 清空文本框
                field.setText("");//将文本框设置为空
                break;
            case "B":  // 退格功能
                // 获取文本框内容
                String numText = field.getText();
                // 如果内容不为空
                if (numText.length() != 0) {
                    // 去除最后一个字符
                    //不包含最后
                    String lastTest = numText.substring(0, numText.length() - 1);
                    field.setText(lastTest);
                }
                break;
            case "T":  // 时间按钮功能（实时显示当前时间）
                    // 1. 停止已有计时器（防止重复创建导致多个计时器同时运行）
                    // - 检查timer对象是否已存在
                    // - 如果存在则停止，避免内存泄漏和多个计时器冲突
                    if (timer != null) {
                        timer.stop();  // 停止计时器任务
                    }

                    // 2. 创建新的计时器（javax.swing.Timer）
                    // - 参数1: 1000表示间隔时间(毫秒)，这里是1秒
                    // - 参数2: 使用Lambda表达式定义定时执行的任务
                    timer = new Timer(1000, e -> {
                        // 2.1 获取当前系统时间
                        // - SimpleDateFormat定义时间格式：年-月-日 时:分:秒
                        // - new Date()获取当前系统时间
                        String currentTime = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                                            .format(new java.util.Date());
                        
                        // 2.2 更新时间显示
                        // - 将格式化后的时间字符串显示在计算器主文本框
                        field.setText(currentTime);
                        
                        // 注意：这里不需要手动刷新界面
                        // Swing的Timer会自动在事件调度线程(EDT)执行，线程安全
                    });

                    // 3. 启动计时器
                    // - 开始每隔1秒执行一次上面定义的任务
                    timer.start();  
                    
                    // 4. 退出switch语句
                    break;
            case "S":  // 音乐播放功能
                try {
                    if (music == null) {  // 第一次点击时加载音乐
                        // 从文件获取音频输入流
                        // 通过 Java Sound API 的核心类AudioSystem获取一个Clip对象
                        // Clip是 Java 中用于播放音频的接口，支持低延迟、循环播放和精确控制
                        // music通常是一个已声明的Clip类型变量
                        AudioInputStream audio = AudioSystem.getAudioInputStream(
                        new File("d:\\临时文件\\张碧晨 - 年轮(1).wav")); 
                        // 获取音频剪辑对象
                        music = AudioSystem.getClip();
                        // 打开音频流
                        music.open(audio);
                    }
                    
                    // 切换音乐播放/暂停状态
                    if (music.isRunning()) {
                        music.stop();
                    } else {
                        music.start();
                    }
                } catch (Exception e) {
                    field.setText("播放失败");
                }
                break;
            case "=":  // 计算功能
                // 执行计算
                calculate(); 
                break;
            case "sin":  // 正弦函数
                if (!currentText.isEmpty()) {
                    try {
                    double num = Double.parseDouble(currentText);
                    // 1. 计算正弦值并保存结果
                    double resultValue = Math.sin(Math.toRadians(num));
                    // 2. 显示结果
                    field.setText(String.valueOf(resultValue));
                    // 3. 记录完整计算过程
                    historyArea.append("sin(" + currentText + ") = " + resultValue + "\n");
                } catch (NumberFormatException e) {
                    field.setText("输入错误");
                }
                }
            break;


        case "cos":
        if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            double result = Math.cos(Math.toRadians(num));
            field.setText(String.valueOf(result));
            historyArea.append("cos(" + currentText + ") = " + result + "\n");
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
            case "tan":
       if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            double result = Math.tan(Math.toRadians(num));
            field.setText(String.valueOf(result));
            historyArea.append("tan(" + currentText + ") = " + result + "\n");
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
            case "log":
    if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            if (num <= 0) {
                field.setText("输入必须>0");
            } else {
                double result = Math.log10(num);
                field.setText(String.valueOf(result));
                historyArea.append("log(" + currentText + ") = " + result + "\n");
            }
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
            case "ln":
    if (!currentText.isEmpty()) {
        try {
            double num = Double.parseDouble(currentText);
            if (num <= 0) {
                field.setText("输入必须>0");
            } else {
                double result = Math.log(num);
                field.setText(String.valueOf(result));
                historyArea.append("ln(" + currentText + ") = " + result + "\n");
            }
        } catch (NumberFormatException e) {
            field.setText("输入错误");
        }
    }
    break;
                
            default:
                // 获取当前文本框内容
                String oldText = field.getText();
                // 获取当前文本的最后一个字符（如果有的话）
                char lastChar = oldText.isEmpty() ? '\0' : oldText.charAt(oldText.length() - 1);
                // 处理小数点自动补零
                if (text.equals(".")) {
                    if (oldText.isEmpty()) {  // 如果当前为空，直接补"0."
                        field.setText("0.");
                        break;
                    }
                    // 如果前面是操作符（如+、-、×、÷），也补"0."
                    else if ("+-×÷".indexOf(lastChar) != -1) {
                        field.setText(oldText + "0.");
                        break;
                    }
                }
                // 检查输入是否合法，合法则添加到文本框
                if (isValidInput(oldText, text, lastChar)) { 
                    field.setText(oldText + text);
                }
                break;
        }
    }
    
    /**
     * 检查输入是否合法的方法
     * @param currentText 当前文本框内容
     * @param newInput 新输入的字符
     * @param lastChar 当前最后一个字符
     * @return true=允许输入, false=禁止输入
     */
    // 增加内容：输入合法性检查
    private boolean isValidInput(String currentText, String newInput, char lastChar) {
        // 规则1：不能以 "+", "×", "÷" 开头（但 "-" 可以表示负数）
        if (currentText.isEmpty() && "+×÷".contains(newInput)) {
            return false;
        }

        // 规则2：不能连续输入操作符（如 ++, +×, ×÷ 等）
        if ("+-×÷".contains(newInput) && "+-×÷".indexOf(lastChar) != -1) {
            return false;
        }

        // 规则3：小数点不能重复输入（如 3..14, 5.2.1）
        if (newInput.equals(".")) {
            // 检查当前数字是否已经有小数点
            String[] parts = currentText.split("[+\\-×÷]");
            String lastNum = parts[parts.length - 1];
            if (lastNum.contains(".")) {
                return false;
            }
        }

        return true; // 默认允许输入
    }
    
    // 执行计算的方法
    private void calculate() {
        // 获取文本框中的表达式
        String expression = field.getText();
        // 替换表达式中的乘号和除号为JavaScript支持的符号
        expression = expression.replace("×", "*").replace("÷", "/");
        // 创建脚本引擎管理器
        ScriptEngineManager manager = new ScriptEngineManager();
        // 获取JavaScript脚本引擎
        ScriptEngine engine = manager.getEngineByName("javascript");
        try {
            // 计算表达式并获取结果
            Object result = engine.eval(expression);
            // 将结果设置到文本框
            field.setText(result.toString());

            // 添加到历史记录
            String historyEntry = expression + " = " + result + "\n";
            historyList.add(historyEntry);
            // 在历史记录面板中添加历史记录
            historyArea.append(historyEntry); 
        } catch (ScriptException e) {
            // 计算异常处理，显示运算错误
            field.setText("运算错误");
            e.printStackTrace();
        }
    }
    
    // 实现ActionListener接口的 actionPerformed 方法，处理按钮点击事件
    @Override
    public void actionPerformed(ActionEvent a) {
        // 播放按钮音效
        playButtonSound();  
        // 获取事件源
        Object source = a.getSource();
        // 检查事件源是否为按钮
        if (source instanceof JButton) {
            // 转换为JButton类型
            JButton btn = (JButton) source;
            // 获取按钮文本
            String text = btn.getText();
            if (text.equals("E")) {
                     clearHistory();
             } else {

            // 处理按钮文本


            setBtnText(text);
             }
        }
    }
    
    // 重写dispose方法，在窗口关闭时释放资源
    @Override
    public void dispose() {
        if (music != null) music.close();
        super.dispose();
    }
    
    // 主方法，程序入口点
    public static void main(String[] args) {
        // 使用SwingUtilities.invokeLater确保在事件调度线程中创建和更新Swing组件
        SwingUtilities.invokeLater(Calculator::new);
    }
}